# -*- coding=utf-8 -*-
# Created Time: 2016年08月23日 星期二 10时34分03秒
# File Name: index.py

"""
测试包
"""


from utils.routes      import route
from utils             import mongo
from utils.errors      import err_param
from utils.lib_time    import dt_str_now
from utils.pages       import pages_count
from utils.lib_re      import high2max, regex_char_trans
from utils.lib_exception import FormatException, CompareException

from service.base      import AuthHandler
from loog.mylog         import flogger as logger
from configure.config  import DB_NAME, COLL_SEED#, COLLECTION_DATAS

from asyncmongo.errors import IntegrityError

from service.index.record import keep

import json, tornado, pymongo, time


@route(r'/seed/add', name='seed_add')
class SeedAddHandler(AuthHandler):
    """ 添加测试包信息

    Stuff:
        1. max 字段，通过 high 字段　处理补零，用来判断最高版本用
        2. 通过 low&proj_name&channel_ver&build_no&high拼内部版本号

        废弃:
        2. 需要从内部版本号中解析出　渠道号
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            token    = self.request.headers.get('TData',{})
            udict    = json.loads(self.request.body)
            url      = udict['url']
            byte     = udict['bytes']
            filename = udict['file_name']
            name     = udict['name']
            size     = udict['size']
            md5      = udict['md5']
            finger   = udict['finger']
            #version  = udict['version']         # 内部版本号
            low      = udict['low']              # 低版本号
            high     = udict['high']             # 高版本号
            channel_ver = udict['channel_ver']   # 渠道识别码 001/002/003
            proj_name = udict.get('proj_name', '') # 项目识别码
            build_no  = udict['build_no']          # Build 号
            remark   = udict.get('remark', '')
            created  = dt_str_now()
            username = token['n']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        # 格式化外部版本号并检查版本号的错误
        try:
            low_str, max_str = SeedMng.version_judge(low, high)
        except FormatException, e:
            logger.exception(e)
            self.write({'code':90003, 'data':{'name':'high'}})
            self.finish()
            return
        except CompareException, e:
            logger.exception(e)
            self.write({'code':90004, 'data':{'name':'high'}})
            self.finish()
            return
        except Exception, e:
            logger.exception(e)
            self.write({'code':90003, 'data':{'name':'high'}})
            self.finish()
            return

        """
        # 从内部版本号中解析出 渠道号
        try:
            channel_ver = self.parse_channel_ver(version)
        except Exception, e:
            logger.exception(e)
            self.write({'code':90003, 'data':{'name':'version'}})
            self.finish()
            return
        """


        """
        UPDATE: 2016-11-17
            检测升级包时，不匹配 build 号
            所以此处修改将 build_no 从拼接中去掉

            build 号从此只是个号码，任何用都没有
        """
        # 拼接内部版本号(用于升级检测)
        if proj_name:
            version = low+' '+proj_name+' '+channel_ver#+' B'+build_no
        else:
            version = low+' '+channel_ver#+' B'+build_no
        logger.info('version: %s' % version)

        # 真正的内部版本号 存着，免得以后有什么用
        version_real = version + ' B' + build_no

        seed_dict = {
            'name'    :name,
            'md5'     :md5,
            'size'    :size,
            'finger'  :finger,
            'version' :version,
            'created' :created,
            'remark'  :remark,
            'url'     :url,
            'bytes'   :byte,
            'low'     :low,
            'high'    :high,
            'ts'      :str(int(time.time())*1000),
            'max_str' :max_str,
            'filename':filename,
            'tested'  :0,
            'channel_ver' :channel_ver,
            'proj_name'   :proj_name,
            'build_no'    :build_no,
            'version_real':version_real
        }

        # 要 存储到 datas 中的数据
        data_dict = {
            "info" : remark,
            "status" : "0",            # 0/1  未激活/激活
            "device_scope" : "1",      # 仅限白名单测试
            "update_success" : 0,      # 更新成功次数
            "download_url" : url,
            "finger_point" : finger,
            "update_failed" : 0,       # 更新失败次数
            "scope" : "test", # 设置域为测试环境,待确定可用后人工转为正式域
            "size" : str(size),
            "md5" : md5
        }

        note = {
            'created' :dt_str_now(),
            'type'    :'上传包',
            'obj'     :'测试包',
            'username':username,
            'keyword' :'上传包 测试包'
        }

        """
        status, info = check_filename(filename)
        if not status:
            self.write(err_param)
            self.finish()
            return
        """

        #data_dict.update(info)
        #seed_dict.update(info)

        try:
            # v2
            yield tornado.gen.Task(mongo.insert, DB_NAME, COLL_SEED, seed_dict)
            # v1 插入到 datas 集合,兼容 v1　版本
            # 更新 v2,v1 完全没关系，只要保证v1能正常使用即可
            # yield tornado.gen.Task(mongo.insert,DB_NAME,COLLECTION_DATAS,data_dict)
            yield tornado.gen.Task(keep, note)
        except IntegrityError, e:   # 添加不允许的重复值，报错并返回详情
            logger.exception(e)
            self.write({'code':90001, 'data':e.msg})
            self.finish()
            return
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({'code':200})
        self.finish()

    def parse_channel_ver(self, version):
        """ 从内部版本号中解析出　渠道号

        内部版本号, 两种格式:
            1. 0.7.0.6 001 B218
            2. 0.7.0.6 cw 001 B218

            -> 001 就是需要的渠道号了
        """

        foo = version.split(' ')
        if len(foo) == 3:
            ver = foo[1]
        elif len(foo) == 4:
            ver = foo[2]
        else:
            raise ValueError('wrong version: %s' % version)

        return ver


@route(r'/seed/list', name='seed_list')
class SeedListHandler(AuthHandler):
    """ 测试包列表

    搜索:
        1. 根据条目名搜索
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict    = json.loads(self.request.body)
            keyword  = udict.get('keyword')
            page     = udict.get('page',1)
            limit    = udict.get('page_size', 15)
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        skip  = (page-1)*limit
        query = {}
        if keyword:
            keyword = regex_char_trans(keyword)
            query.update({'name': {'$regex':keyword}})

        try:
            # 添加时间倒序排
            sort = [['created', pymongo.DESCENDING]]
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_SEED,
                                        query,
                                        skip=skip,
                                        limit=limit,
                                        sort=sort)
            count = yield tornado.gen.Task(
                                        mongo.count,
                                        DB_NAME,
                                        COLL_SEED,
                                        sort,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        pages = pages_count(count, limit)
        self.write({
            'code':200,
            'page':page,
            'page_size':limit,
            'pages':pages,
            'data':seeds
        })
        self.finish()


@route(r'/seed/list/push', name='seed_list_push')
class SeedListPushHandler(AuthHandler):
    """ 测试包列表 - 推送至此类模块使用

    搜索:
        1. 根据包条目名搜索

        正式渠道:
            1. 至少在测试类有一次推送的包才能显示
            2. 渠道号与包的渠道号相同的才能显示
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict    = json.loads(self.request.body)
            env      = udict.get('env', 'online')
            keyword  = udict.get('keyword')
            page     = udict.get('page',1)
            limit    = udict.get('page_size', 15)
            channel_ver = udict.get('channel_ver', '')
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        skip  = (page-1)*limit
        query = {}
        if keyword:
            keyword = regex_char_trans(keyword)
            query.update({'name': {'$regex':keyword}})

        if env == 'online':
            query.update({
                'tested'     :1,           # 正式渠道只获取　经过测试的包
                'channel_ver':channel_ver  # 渠道号要和内部版本号中的渠道号一致
            })

        try:
            sort = [['created', pymongo.DESCENDING]]
            fields = {'name':1, 'remark':1}
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_SEED,
                                        query,
                                        skip=skip,
                                        limit=limit,
                                        sort=sort,
                                        fields=fields)
            count = yield tornado.gen.Task(
                                        mongo.count,
                                        DB_NAME,
                                        COLL_SEED,
                                        sort,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        pages = pages_count(count, limit)
        self.write({
            'code':200,
            'page':page,
            'page_size':limit,
            'pages':pages,
            'data':seeds
        })
        self.finish()


@route(r'/seed/detail', name='seed_detail')
class SeedDetailHandler(AuthHandler):
    """ 测试包－详情 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict = json.loads(self.request.body)
            fid   = udict['id']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        try:
            seed = yield tornado.gen.Task(
                                        mongo.find_one,
                                        DB_NAME,
                                        COLL_SEED,
                                        {'_id':fid})
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({
            'code':200,
            'data':seed
        })
        self.finish()


@route(r'/seed/edit', name='seed_edit')
class SeedEditHandler(AuthHandler):
    """ 测试包 - 编辑 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict    = json.loads(self.request.body)
            fid      = udict['id']
            name     = udict['name']
            finger   = udict['finger']
            remark   = udict['remark']
            low      = udict['low']
            high     = udict['high']

            channel_ver = udict['channel_ver']       # 渠道识别码 001/002/003
            proj_name   = udict.get('proj_name', '') # 项目识别码
            build_no    = udict['build_no']          # Build 号
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        # 格式化外部版本号并检查版本号的错误
        try:
            low_str, max_str = SeedMng.version_judge(low, high)
        except FormatException, e:
            logger.exception(e)
            self.write({'code':90003, 'data':{'name':'high'}})
            self.finish()
            return
        except CompareException, e:
            logger.exception(e)
            self.write({'code':90004, 'data':{'name':'high'}})
            self.finish()
            return
        except Exception, e:
            logger.exception(e)
            self.write({'code':90003, 'data':{'name':'high'}})
            self.finish()
            return

        """
        UPDATE: 2016-11-17
            检测升级包时，不匹配 build 号
            所以此处修改将 build_no 从拼接中去掉

            build 号从此只是个号码，任何用都没有
        """
        # 拼接内部版本号(用于升级检测)
        if proj_name:
            version = low+' '+proj_name+' '+channel_ver#+' B'+build_no
        else:
            version = low+' '+channel_ver#+' B'+build_no
        logger.info('version: %s' % version)

        # 真正的内部版本号 存着，免得以后有什么用
        version_real = version + ' B' + build_no

        seed_dict = {
            'name'   :name,
            'finger' :finger,
            'version':version,
            'remark' :remark,
            'low'    :low,
            'high'   :high,
            'max_str':max_str,

            'channel_ver':channel_ver,
            'proj_name'  :proj_name,
            'build_no'   :build_no,
            'version_real':version_real
        }

        try:
            yield tornado.gen.Task(
                                mongo.update,
                                DB_NAME,
                                COLL_SEED,
                                {'_id':fid},
                                seed_dict)
        except IntegrityError, e:
            logger.exception(e)
            self.write({'code':90001, 'data':e.msg})
            self.finish()
            return
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({'code':200})
        self.finish()


class SeedMng(object):
    """ 测试包管理 """

    @classmethod
    def version_judge(cls, low, high):
        """ 外部版本号格式化并检查大小"""

        low_str  = high2max(low)
        max_str  = high2max(high)
        if len(low_str) !=16 or len(max_str) != 16:
            raise FormatException('Wrong version format')
        if not max_str > low_str:
            raise CompareException('Wrong version num')

        return low_str, max_str
